﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
//
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
//
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
using System.Runtime.InteropServices.ComTypes;
using Camera_NET;
using DirectShowLib;


namespace iPublic
{
    /// <summary>
    /// 摄像头控制，从国外一个开源项目拿来用的
    /// 网址：https://www.codeproject.com/Articles/671407/Camera_Net-Library
    /// 修改记录：
    ///   20171212，zch，创建基本功能
    /// </summary>
    public partial class iCamera : Component
    {
        #region //Vars，变量常量定义
        public CameraControl cameraControl = null;
        // Camera object
        //private Camera _Camera;

        // Rect selection with mouse
        public NormalizedRect _MouseSelectionRect = new NormalizedRect(0, 0, 0, 0);
        public bool _bDrawMouseSelection = false;

        // Zooming
        public bool _bZoomed = false;
        public double _fZoomValue = 1.0;

        // Camera choice
        public CameraChoice _CameraChoice = new CameraChoice();

        //提示信息
        public iDefine.showInfoProc showInfoProc = null;
        #endregion

        public iCamera(CameraControl ACameraControl)
        {   //初始化
            if (ACameraControl != null) cameraControl = ACameraControl;
        }
        ~iCamera()
        {   //释放
            if (_CameraChoice != null) { _CameraChoice.Dispose(); _CameraChoice = null; }
        }    

        public int Start(IMoniker AMoniker=null, Resolution AFenbianlv=null)
        {
            int result = -1;
            if (cameraControl.CameraCreated) return 1;
            //选择摄像头
            IMoniker moniker = AMoniker;
            if (_CameraChoice.Devices.Count > 0) moniker = _CameraChoice.Devices[0].Mon;
            //摄像头的分辨率
            Resolution fenbianlv = AFenbianlv;
            SetCamera(moniker, fenbianlv);
            //
            result = 0;
            return 0;
        }


        public int PaiZhao(ref Bitmap bitmap, string sFile)
        {
            int result = -1;
            showInfo("正在拍照...");
            if (!cameraControl.CameraCreated)
            {
                showInfo("尚未开启摄像头");
                return 1;
            }

            //Bitmap bitmap = null;
            //string sFile = "D:\\" + cameraControl.Resolution.ToString() + "-" + DateTime.Now.ToString("yyyyMMdd_hhmmss") + ".png";
            try
            {
                bitmap = null;
                bitmap = cameraControl.SnapshotSourceImage();   //SnapshotSourceImage、SnapshotOutputImage
                if (!string.IsNullOrEmpty(sFile)) bitmap.Save(sFile,ImageFormat.Jpeg);
                //
                result = 0;
            }
            catch (Exception ex)
            {
                //MessageBox.Show(ex.Message, @"Error while getting a snapshot");
                showInfo("【警告】拍照出错："+ex.Message);
            }

            if (bitmap == null) return result;
            //完成
            showInfo("抓图完毕。" + sFile);
            return result;
        }

        public int SetCamera(IMoniker camera_moniker, Resolution resolution)
        {
            int result = -1;
            try
            {
                if (camera_moniker == null)
                {   //如果未传入
                    _CameraChoice.UpdateDeviceList();
                    if (_CameraChoice.Devices.Count > 0) camera_moniker = _CameraChoice.Devices[0].Mon;
                }
                // NOTE: You can debug with DirectShow logging:
                //cameraControl.DirectShowLogFilepath = @"C:\YOUR\LOG\PATH.txt";

                // Makes all magic with camera and DirectShow graph
                cameraControl.SetCamera(camera_moniker, resolution);
                //
                result = 0;
            }
            catch (Exception e)
            {
                //MessageBox.Show(e.Message, @"Error while running camera");
                iPublicX.showMessage(e.Message, "Error while running camera");
            }

            if (!cameraControl.CameraCreated) return 2;

            // If you are using Direct3D surface overlay
            // (see documentation about rebuild of library for it)
            //cameraControl.UseGDI = false;

            cameraControl.MixerEnabled = true;
            cameraControl.OutputVideoSizeChanged += Camera_OutputVideoSizeChanged;
            UpdateCameraBitmap();
            // gui update
            UpdateGUIButtons();

            //
            return 0;
        }


        #region //fillFenBianLv，填充分辨率
        public int FillFenBianLv(ComboBox cbx)
        {   //填充分辨率
            int result = -1, index_to_select = -1;
            if (!cameraControl.CameraCreated) return 1;
            //
            ResolutionList resolutions = Camera.GetResolutionList(cameraControl.Moniker);
            if (resolutions == null) return 2;

            //
            if (cbx == null) return 0;
            cbx.Items.Clear();
            for (int index = 0; index < resolutions.Count; index++)
            {
                cbx.Items.Add(resolutions[index].ToString());
                if (resolutions[index].CompareTo(cameraControl.Resolution) == 0)
                {
                    index_to_select = index;
                }
            }

            // select current resolution
            if (index_to_select >= 0)
            {
                cbx.SelectedIndex = index_to_select;
            }
            //完成
            return 0;
        }
        #endregion


        #region //FillCameraList，填充摄像头清单
        //private CameraChoice cameraChoice = new CameraChoice();
        public int FillCameraList(ComboBox cbx)
        {
            int result = -1;
            _CameraChoice.UpdateDeviceList();        //更新一遍
            if (cbx == null) return 0;

            //
            cbx.Items.Clear();
            foreach (var camera_device in _CameraChoice.Devices)
            {
                cbx.Items.Add(camera_device.Name);
            }
            //
            return 0;
        }
        #endregion
        
        public void showInfo(string sInfo, bool lTime = true)
        {
            if (showInfoProc == null) return;
            showInfoProc(sInfo, lTime);
        }



        #region Camera event handlers

        // Event handler for OutputVideoSizeChanged event
        private void Camera_OutputVideoSizeChanged(object sender, EventArgs e)
        {
            // Update camera's bitmap (new size needed)
            UpdateCameraBitmap();

            // Place Zoom button in correct place on form
            UpdateUnzoomButton();
        }

        #endregion

        #region Overlay bitmaps stuff

        // Generate bitmap for overlay
        private void UpdateCameraBitmap()
        {
            if (!cameraControl.MixerEnabled)
                return;

            cameraControl.OverlayBitmap = GenerateColorKeyBitmap(false);

            #region D3D bitmap mixer
            //if (cameraControl.UseGDI)
            //{
            //    cameraControl.OverlayBitmap = GenerateColorKeyBitmap(false);
            //}
            //else
            //{
            //    cameraControl.OverlayBitmap = GenerateAlphaBitmap();
            //}
            #endregion
        }

        // NOTE: This function is an example of overlay bitmap usage
        // Create bitmap with selection rect, text or any other overlay
        private Bitmap GenerateColorKeyBitmap(bool useAntiAlias)
        {
            int w = cameraControl.OutputVideoSize.Width;
            int h = cameraControl.OutputVideoSize.Height;

            if (w <= 0 || h <= 0)
                return null;

            // Create RGB bitmap
            Bitmap bmp = new Bitmap(w, h, PixelFormat.Format24bppRgb);
            Graphics g = Graphics.FromImage(bmp);

            // configure antialiased drawing or not
            if (useAntiAlias)
            {
                g.SmoothingMode = SmoothingMode.AntiAlias;
                g.TextRenderingHint = TextRenderingHint.AntiAlias;
            }
            else
            {
                g.SmoothingMode = SmoothingMode.None;
                g.TextRenderingHint = TextRenderingHint.SingleBitPerPixelGridFit;
            }

            // Clear the bitmap with the color key
            g.Clear(cameraControl.GDIColorKey);

            // Draw selection rect --------------------------
            if (_bDrawMouseSelection && IsMouseSelectionRectCorrect())
            {
                Color color_of_pen = Color.Gray;
                if (IsMouseSelectionRectCorrectAndGood())
                {
                    color_of_pen = Color.Green;
                }

                Pen pen = new Pen(color_of_pen, 2.0f);

                Rectangle rect = new Rectangle(
                        (int)(_MouseSelectionRect.left * w),
                        (int)(_MouseSelectionRect.top * h),
                        (int)((_MouseSelectionRect.right - _MouseSelectionRect.left) * w),
                        (int)((_MouseSelectionRect.bottom - _MouseSelectionRect.top) * h)
                    );



                g.DrawLine(pen, rect.Left - 5, rect.Top, rect.Right + 5, rect.Top);
                g.DrawLine(pen, rect.Left - 5, rect.Bottom, rect.Right + 5, rect.Bottom);
                g.DrawLine(pen, rect.Left, rect.Top - 5, rect.Left, rect.Bottom + 5);
                g.DrawLine(pen, rect.Right, rect.Top - 5, rect.Right, rect.Bottom + 5);

                pen.Dispose();

            }

            // Draw zoom text if needed
            if (_bZoomed)
            {
                Font font = new Font("Tahoma", 16);
                Brush textColorKeyed = new SolidBrush(Color.DarkBlue);

                g.DrawString("Zoom: " + Math.Round(_fZoomValue, 1).ToString("0.0") + "x", font, textColorKeyed, 4, 4);

                font.Dispose();
                textColorKeyed.Dispose();
            }

            // Draw text logo for example
            {
                Font font = new Font("Tahoma", 16);
                Brush textColorKeyed = new SolidBrush(Color.Yellow);

                //Sample project for Camera_NET component
                g.DrawString("海宏软件", font, textColorKeyed, 4, h - 30);

            }


            // dispose Graphics
            g.Dispose();

            // return the bitmap
            return bmp;
        }

        #region D3D bitmap mixer
        //public Bitmap GenerateAlphaBitmap()
        //{
        //    // Alpha values
        //    int alpha50 = (int)(255 * 0.50f); // 50% opacity

        //    // Some drawing tools needed later
        //    Pen blackBorder = new Pen(Color.Black, 2.0f);
        //    Brush red50 = new SolidBrush(Color.FromArgb(alpha50, Color.Red));
        //    Font font = new Font("Tahoma", 16);

        //    int w = _Camera.OutputVideoSize.Width;
        //    int h = _Camera.OutputVideoSize.Height;

        //    // Create a ARGB bitmap
        //    Bitmap bmp = new Bitmap(w, h, PixelFormat.Format32bppArgb);
        //    Graphics g = Graphics.FromImage(bmp);

        //    // Do antialiased drawings
        //    g.SmoothingMode = SmoothingMode.AntiAlias;
        //    g.TextRenderingHint = TextRenderingHint.AntiAlias;

        //    // Clear the bitmap with complete transparency
        //    g.Clear(Color.Transparent);

        //    // Draw a green circle with black border in the middle
        //    //g.FillEllipse(green, 320 * w / 640, 240 * h / 480, 155 * w / 640, 155 * h / 480);
        //    g.FillEllipse(red50, w / 2 - 70, h / 2 - 70, 140, 140);
        //    g.DrawEllipse(blackBorder, w / 2 - 70, h / 2 - 70, 140, 140);


        //    // Release GDI+ objects
        //    blackBorder.Dispose();
        //    red50.Dispose();
        //    g.Dispose();

        //    // return the bitmap
        //    return bmp;
        //}
        #endregion

        // Swritch GDI/D3D
        //private void button3_Click(object sender, EventArgs e)
        //{
        //    cameraControl.MixerEnabled = false;

        //    cameraControl.UseGDI = !cameraControl.UseGDI;
        //    UpdateCameraBitmap();

        //    if (!cameraControl.MixerEnabled)
        //        cameraControl.MixerEnabled = true;
        //}

        #endregion

        #region Mouse selection stuff



        private void cameraControl_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button != MouseButtons.Left)
                return;

            if (!cameraControl.CameraCreated)
                return;

            if (_bZoomed)
                return;

            PointF point = cameraControl.ConvertWinToNorm(new PointF(e.X, e.Y));
            _MouseSelectionRect = new NormalizedRect(point.X, point.Y, point.X, point.Y);

            _bDrawMouseSelection = true;
            UpdateCameraBitmap();
        }

        private void cameraControl_MouseUp(object sender, MouseEventArgs e)
        {
            if (_bZoomed)
                return;

            if (!_bDrawMouseSelection)
                return;

            // Zoom
            if (!IsMouseSelectionRectCorrectAndGood())
            {
                // Doesn't allow zoom too much

                _bDrawMouseSelection = false;
                UpdateCameraBitmap();
                return;
            }


            int w = cameraControl.Resolution.Width;
            int h = cameraControl.Resolution.Height;

            double rect_w = w * (_MouseSelectionRect.right - _MouseSelectionRect.left);
            double rect_h = h * (_MouseSelectionRect.bottom - _MouseSelectionRect.top);


            // Save frame proportion

            double ratio_video = ((double)w) / h;
            double ratio_rect = ((double)rect_w) / rect_h;

            //NormalizedRect norm_rect;



            if (ratio_video >= ratio_rect)
            {
                // calculate width
                double needed_rect_width = rect_h * ratio_video;

                _MouseSelectionRect.left -= (float)(((needed_rect_width - rect_w) / 2) / w);
                _MouseSelectionRect.right += (float)(((needed_rect_width - rect_w) / 2) / w);

                _fZoomValue = (double)h / rect_h;
            }
            else
            {
                // calculate height
                double needed_rect_height = rect_w / ratio_video;

                _MouseSelectionRect.top -= (float)(((needed_rect_height - rect_h) / 2) / h);
                _MouseSelectionRect.bottom += (float)(((needed_rect_height - rect_h) / 2) / h);

                _fZoomValue = (double)w / rect_w;
            }



            Rectangle rect = new Rectangle(
                    (int)(_MouseSelectionRect.left * w),
                    (int)(_MouseSelectionRect.top * h),
                    (int)((_MouseSelectionRect.right - _MouseSelectionRect.left) * w),
                    (int)((_MouseSelectionRect.bottom - _MouseSelectionRect.top) * h)
                );


            cameraControl.ZoomToRect(rect);
            _bZoomed = true;
            _bDrawMouseSelection = false;

            UpdateCameraBitmap();

            // Place Zoom button in correct place on form
            UpdateUnzoomButton();

        }

        private void cameraControl_MouseMove(object sender, MouseEventArgs e)

        {
            if (e.Button != MouseButtons.Left)
                return;

            if (!cameraControl.CameraCreated)
                return;

            if (_bZoomed)
                return;

            if (!_bDrawMouseSelection)
                return;

            PointF point = cameraControl.ConvertWinToNorm(new PointF(e.X, e.Y));
            _MouseSelectionRect.right = point.X;
            _MouseSelectionRect.bottom = point.Y;


            UpdateCameraBitmap();
        }

        private bool IsMouseSelectionRectCorrect()
        {
            if (Math.Abs(_MouseSelectionRect.right - _MouseSelectionRect.left) < float.Epsilon * 10 ||
                Math.Abs(_MouseSelectionRect.bottom - _MouseSelectionRect.top) < float.Epsilon * 10)
            {
                return false;
            }

            if (_MouseSelectionRect.left >= _MouseSelectionRect.right ||
                _MouseSelectionRect.top >= _MouseSelectionRect.bottom)
            {
                return false;
            }

            if (_MouseSelectionRect.left < 0 ||
                _MouseSelectionRect.top < 0 ||
                _MouseSelectionRect.right > 1.0 ||
                _MouseSelectionRect.bottom > 1.0)
            {
                return false;
            }
            return true;
        }

        private bool IsMouseSelectionRectCorrectAndGood()
        {
            if (!IsMouseSelectionRectCorrect())
            {
                return false;

            }

            // Zoom
            if (Math.Abs(_MouseSelectionRect.right - _MouseSelectionRect.left) < 0.1f ||
                Math.Abs(_MouseSelectionRect.bottom - _MouseSelectionRect.top) < 0.1f)
            {
                return false;
            }

            return true;
        }

        #endregion

        #region Zooming stuff

        // Unzoom on video double-click
        private void cameraControl_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            UnzoomCamera();
        }

        // Unzoom with unzoom button
        private void buttonUnZoom_Click(object sender, EventArgs e)
        {
            UnzoomCamera();
        }


        // Unzoom camera and update gui
        private void UnzoomCamera()
        {
            cameraControl.ZoomToRect(new Rectangle(0, 0, cameraControl.Resolution.Width, cameraControl.Resolution.Height));

            _bZoomed = false;
            _fZoomValue = 1.0;

            // gui updates
            UpdateCameraBitmap();
            UpdateUnzoomButton();

            _bDrawMouseSelection = false;
        }


        // Place Zoom button in correct place on form
        private void UpdateUnzoomButton()
        {
            //if (_bZoomed)
            //{
            //    buttonUnZoom.Left = cameraControl.Left + (cameraControl.ClientRectangle.Width - cameraControl.OutputVideoSize.Width) / 2 + 4;
            //    buttonUnZoom.Top = cameraControl.Top + (cameraControl.ClientRectangle.Height - cameraControl.OutputVideoSize.Height) / 2 + 40;
            //    buttonUnZoom.Visible = true;
            //}
            //else
            //{
            //    buttonUnZoom.Visible = false;
            //}
        }

        #endregion

        private void UpdateGUIButtons()
        {
            //buttonCrossbarSettings.Enabled = (cameraControl.CrossbarAvailable);
        }

    }

}